/*
 *  quaternion.cc
 *  wonderland-project
 *
 *  Created by Ben Nolan on 14/10/08.
 *  Copyright 2008 Nolan Consulting Limited. All rights reserved.
 *
 */

#include "quaternion.h"

#include <v8.h>
#include <OgreVector3.h>
#include <OgreQuaternion.h>
#include "quaternion.h"
#include "vector.h"
#include "v8defines.h"
#include "scripting_context.h"

namespace JSQuaternion{

  v8::Handle<v8::ObjectTemplate> instance_template;
  v8::Handle<v8::FunctionTemplate> function_template;
  v8::Persistent<v8::Context> context_;

  V8CALLBACK(Quaternion,getYaw){ 
    return v8::Number::New(Ogre::Degree(V8INTERNAL(Ogre::Quaternion*)->getYaw()).valueDegrees());
  }

  V8CALLBACK(Quaternion,yaw){ 
    if (args.Length() != 1)
      return v8::Undefined();

    Ogre::Quaternion q(Ogre::Degree(args[0]->NumberValue()), Ogre::Vector3::UNIT_Y);

    return JSQuaternion::New(*V8INTERNAL(Ogre::Quaternion*) * q);
  }

  V8CALLBACK(Quaternion,getPitch){ 
    return v8::Number::New(Ogre::Degree(V8INTERNAL(Ogre::Quaternion*)->getPitch()).valueDegrees());
  }

  V8CALLBACK(Quaternion,pitch){ 
    if (args.Length() != 1)
      return v8::Undefined();

    Ogre::Quaternion q(Ogre::Degree(args[0]->NumberValue()), Ogre::Vector3::UNIT_X);

    return JSQuaternion::New(*V8INTERNAL(Ogre::Quaternion*) * q);
  }

  V8CALLBACK(Quaternion,getRoll){ 
    return v8::Number::New(Ogre::Degree(V8INTERNAL(Ogre::Quaternion*)->getRoll()).valueDegrees());
  }

  V8CALLBACK(Quaternion,roll){ 
    if (args.Length() != 1)
      return v8::Undefined();

    Ogre::Quaternion q(Ogre::Degree(args[0]->NumberValue()), Ogre::Vector3::UNIT_Z);

    return JSQuaternion::New(*V8INTERNAL(Ogre::Quaternion*) * q);
  }

  V8CALLBACK(Quaternion,multiply){ 
    if (!V8ARGUMENTTYPE("Vector", 0))
      return ThrowException(v8::String::New("Invalid argument"));

    Ogre::Vector3 v = *V8ARGUMENT(Ogre::Vector3*, 0);
    
    return JSVector::New(*V8INTERNAL(Ogre::Quaternion*) * v);
  }

  V8_GETTER(Quaternion,w){
    return v8::Number::New(V8INTERNAL(Ogre::Quaternion*)->w);
  }

  V8_SETTER(Quaternion,w){
    V8INTERNAL(Ogre::Quaternion*)->w = value->NumberValue();
  }

  V8_GETTER(Quaternion,x){
    return v8::Number::New(V8INTERNAL(Ogre::Quaternion*)->x);
  }

  V8_SETTER(Quaternion,x){
    V8INTERNAL(Ogre::Quaternion*)->x = value->NumberValue();
  }

  V8_GETTER(Quaternion,y){
    return v8::Number::New(V8INTERNAL(Ogre::Quaternion*)->y);
  }

  V8_SETTER(Quaternion,y){
    V8INTERNAL(Ogre::Quaternion*)->y = value->NumberValue();
  }

  V8_GETTER(Quaternion,z){
    return v8::Number::New(V8INTERNAL(Ogre::Quaternion*)->z);
  }

  V8_SETTER(Quaternion,z){
    V8INTERNAL(Ogre::Quaternion*)->z = value->NumberValue();
  }

  /* Sets internal fields with our idiom of storing the class name in the second field */
  void setInternalFields(v8::Persistent<v8::Object> p, Ogre::Quaternion v){
    p->SetInternalField(0, v8::External::New(new Ogre::Quaternion(v.ptr())));
    p->SetInternalField(1, v8::String::New("Quaternion"));
  }
  
  /* Construct a new instance of a vector */
  V8CALLBACK(Quaternion,create){
    if (!args.IsConstructCall()) 
      return ThrowException(v8::String::New("Cannot call constructor as function"));

    v8::Persistent<v8::Object> self = v8::Persistent<v8::Object>::New(args.Holder());

    setInternalFields(self, Ogre::Quaternion::IDENTITY);

    return self;
  }

  /* Called from c++ */
  v8::Handle<v8::Value> New(Ogre::Quaternion v){
    context_->Enter();

    v8::Persistent<v8::Object> instance = v8::Persistent<v8::Object>::New(function_template->GetFunction()->NewInstance());
    setInternalFields(instance, v);
    return instance;
  }

  /* Create the function and instance templates and register the Quaternion constructor */

  void Initialize(Wonderland::ScriptingContext*sc){
    context_ = sc->getContext();

    // Function Template
    function_template = v8::FunctionTemplate::New(Quaternion_create);
    function_template->SetClassName(v8::String::New("Quaternion"));

    // Instance template
    instance_template = function_template->InstanceTemplate();
    instance_template->SetInternalFieldCount(2);

    instance_template->SetAccessor(v8::String::New("w"), Quaternion_getw, Quaternion_setw);
    instance_template->SetAccessor(v8::String::New("x"), Quaternion_getx, Quaternion_setx);
    instance_template->SetAccessor(v8::String::New("y"), Quaternion_gety, Quaternion_sety);
    instance_template->SetAccessor(v8::String::New("z"), Quaternion_getz, Quaternion_setz);

    #define V8REGISTER(function) instance_template->Set(#function, v8::FunctionTemplate::New(Quaternion_ ## function));

    V8REGISTER(yaw);
    V8REGISTER(pitch);
    V8REGISTER(roll);
    V8REGISTER(getYaw);
    V8REGISTER(getPitch);
    V8REGISTER(getRoll);
    V8REGISTER(multiply);

    // add the constructor
    context_->Enter();
    context_->Global()->Set(v8::String::New("Quaternion"), function_template->GetFunction());
  }

}